package cn.anecansaitin.inet.mixins;

import cn.anecansaitin.inet.TranslateHelper;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import mcjty.lib.base.StyleConfig;
import mcjty.lib.container.GenericContainer;
import mcjty.lib.gui.GenericGuiContainer;
import mcjty.lib.gui.ManualEntry;
import mcjty.lib.gui.Window;
import mcjty.lib.gui.widgets.*;
import mcjty.lib.network.PacketServerCommandTyped;
import mcjty.lib.typed.TypedMap;
import mcjty.lib.varia.BlockPosTools;
import mcjty.lib.varia.Logging;
import mcjty.rftoolsbase.api.xnet.channels.IChannelType;
import mcjty.rftoolsbase.api.xnet.gui.IndicatorIcon;
import mcjty.rftoolsbase.api.xnet.keys.SidedConsumer;
import mcjty.rftoolsbase.api.xnet.keys.SidedPos;
import mcjty.xnet.XNet;
import mcjty.xnet.client.ChannelClientInfo;
import mcjty.xnet.client.ConnectedBlockClientInfo;
import mcjty.xnet.client.ConnectorClientInfo;
import mcjty.xnet.modules.controller.ChannelInfo;
import mcjty.xnet.modules.controller.blocks.TileEntityController;
import mcjty.xnet.modules.controller.client.ChannelEditorPanel;
import mcjty.xnet.modules.controller.client.ConnectorEditorPanel;
import mcjty.xnet.modules.controller.client.GuiController;
import mcjty.xnet.setup.Config;
import mcjty.xnet.setup.XNetMessages;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.List;

import static mcjty.lib.gui.widgets.Widgets.button;
import static mcjty.xnet.modules.controller.client.GuiController.TAG_ENABLED;
import static mcjty.xnet.modules.controller.client.GuiController.TAG_NAME;

@Mixin(GuiController.class)
public abstract class ControllerGuiMixin extends GenericGuiContainer<TileEntityController, GenericContainer> {
    public ControllerGuiMixin(TileEntityController tileEntity, GenericContainer container, Inventory inventory, ManualEntry manualEntry) {
        super(tileEntity, container, inventory, manualEntry);
    }

    @Shadow
    private WidgetList connectorList;
    @Final
    @Shadow
    private List<SidedPos> connectorPositions;
    @Shadow
    private TextField searchBar;
    @Shadow
    private int delayedSelectedChannel;
    @Shadow
    private int delayedSelectedLine;
    @Shadow
    private SidedPos delayedSelectedConnector;
    @Shadow
    private Panel channelEditPanel;
    @Shadow
    private Panel connectorEditPanel;
    @Final
    @Shadow
    private ToggleButton[] channelButtons;
    @Shadow
    private SidedPos editingConnector;
    @Shadow
    private int editingChannel;
    @Shadow
    private int showingChannel;
    @Shadow
    private SidedPos showingConnector;
    @Shadow
    private Button copyConnector;
    @Shadow
    private boolean needsRefresh;
    @Shadow
    private int listDirty;

    @Shadow
    protected abstract boolean listsReady();
    @Shadow
    protected abstract void removeChannel();
    @Shadow
    protected abstract void copyChannel();
    @Shadow
    protected abstract void copyConnector();
    @Shadow
    protected abstract void createChannel(String typeId);
    @Shadow
    protected abstract void pasteChannel();
    @Shadow
    protected abstract int getSelectedChannel();
    @Shadow
    public abstract void refresh();
    @Shadow
    protected abstract void sendSplit(PacketServerCommandTyped packet, boolean doSplit);
    @Shadow
    protected abstract ConnectorClientInfo findClientInfo(ChannelClientInfo info, SidedPos p);
    @Shadow
    protected abstract void removeConnector(SidedPos sidedPos);
    @Shadow
    protected abstract void createConnector(SidedPos sidedPos);
    @Shadow
    protected abstract void pasteConnector();
    @Shadow
    protected abstract void selectConnectorEditor(SidedPos sidedPos, int finalI);
    @Shadow
    protected abstract void initializeFields();
    @Shadow
    protected abstract void setupEvents();

    //INTECT -----------------------------------------------------------------------------------------------------------

    @Override
    public void init() {
        window = new Window(this, tileEntity, XNetMessages.INSTANCE, new ResourceLocation(XNet.MODID, "gui/controller.gui"));
        Panel toplevel = (Panel) window.getToplevel();
        List<Widget<?>> children = toplevel.getChildren();
        //频道选择按钮
        List<Widget<?>> channels = ((Panel) children.get(0)).getChildren();
        channels.get(0).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel0"));
        channels.get(1).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel1"));
        channels.get(2).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel2"));
        channels.get(3).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel3"));
        channels.get(4).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel4"));
        channels.get(5).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel5"));
        channels.get(6).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel6"));
        channels.get(7).getTooltips().set(0, TranslateHelper.getText("inet.controller.button.channel7"));

        super.init();

        initializeFields();
        setupEvents();

        editingConnector = null;
        editingChannel = -1;

        refresh();
        listDirty = 0;
    }

    @Inject(method = "hilightSelectedContainer", at = @At(value = "INVOKE", target = "Lmcjty/lib/varia/Logging;message(Lnet/minecraft/world/entity/player/Player;Ljava/lang/String;)V"), remap = false, cancellable = true)
    private void mixin$hilightSelectedContainer(int index, CallbackInfo ci) {
        Logging.message(this.minecraft.player, TranslateHelper.getText("inet.controller.massage.highlight"));
        this.minecraft.player.closeContainer();
        ci.cancel();
    }

    @Inject(method = "handleClipboard", at = @At(value = "INVOKE", target = "Lmcjty/xnet/modules/controller/client/GuiController;showMessage(Lnet/minecraft/client/Minecraft;Lnet/minecraft/client/gui/screens/Screen;Lmcjty/lib/gui/WindowManager;IILjava/lang/String;)V"), remap = false, cancellable = true)
    private void mixin$handleClipboard(int keyCode, CallbackInfoReturnable<Boolean> cir) {
        GuiController.showMessage(this.minecraft, this, this.getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.null"));
        cir.setReturnValue(true);
    }

    @Inject(method = "removeChannel", at = @At(value = "INVOKE", target = "Lmcjty/xnet/modules/controller/client/GuiController;showMessage(Lnet/minecraft/client/Minecraft;Lnet/minecraft/client/gui/screens/Screen;Lmcjty/lib/gui/WindowManager;IILjava/lang/String;Lmcjty/lib/gui/events/ButtonEvent;)V"), remap = false, cancellable = true)
    private void mixin$removeChannel(CallbackInfo ci) {
        GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.remove_channel.massage") + " " + (this.getSelectedChannel() + 1) + "?", () -> {
            this.sendServerCommandTyped(XNetMessages.INSTANCE, TileEntityController.CMD_REMOVECHANNEL, TypedMap.builder().put(TileEntityController.PARAM_INDEX, this.getSelectedChannel()).build());
            this.refresh();
        });

        ci.cancel();
    }

    @Inject(method = "refreshChannelEditor", at = @At("HEAD"), cancellable = true, remap = false)
    private void mixin$refreshChannelEditor(CallbackInfo ci) {
        if (!listsReady()) {
            ci.cancel();
            return;
        }
        if (editingChannel != -1 && showingChannel != editingChannel) {
            showingChannel = editingChannel;
            channelButtons[editingChannel].pressed(true);

            copyConnector = null;
            channelEditPanel.removeChildren();
            if (channelButtons[editingChannel].isPressed()) {
                ChannelClientInfo info = tileEntity.clientChannels.get(editingChannel);
                if (info != null) {
                    ChannelEditorPanel editor = new ChannelEditorPanel(channelEditPanel, minecraft, ((GuiController) (Object) this), editingChannel);
                    editor.label(TranslateHelper.getText("inet.controller.channel_editor_panel.editor.label.text") + (editingChannel + 1))
                            .shift(5)
                            .toggle(TAG_ENABLED, TranslateHelper.getText("inet.controller.channel_editor_panel.editor.toggle.tooltip"), info.isEnabled())
                            .shift(5)
                            .text(TAG_NAME, TranslateHelper.getText("inet.controller.channel_editor_panel.editor.text.tooltip"), info.getChannelName(), 65);
                    info.getChannelSettings().createGui(editor);

                    Button remove = button(151, 1, 9, 10, "x")
                            .textOffset(0, -1)
                            .tooltips(TranslateHelper.getText("inet.controller.channel_editor_panel.button.remove.tooltip"))
                            .event(this::removeChannel);
                    channelEditPanel.children(remove);
                    editor.setState(info.getChannelSettings());

                    Button copyChannel = button(134, 19, 25, 14, "C")
                            .tooltips(TranslateHelper.getText("inet.controller.channel_editor_panel.button.copy_channel.tooltip1"), TranslateHelper.getText("inet.controller.channel_editor_panel.button.copy_channel.tooltip2"))
                            .event(this::copyChannel);
                    channelEditPanel.children(copyChannel);

                    copyConnector = button(114, 19, 25, 14, "C")
                            .tooltips(TranslateHelper.getText("inet.controller.channel_editor_panel.button.copy_connector.tooltip1"), TranslateHelper.getText("inet.controller.channel_editor_panel.button.copy_connector.tooltip2"))
                            .event(this::copyConnector);
                    channelEditPanel.children(copyConnector);

                } else {
                    ChoiceLabel type = new ChoiceLabel()
                            .hint(5, 3, 95, 14);
                    for (IChannelType channelType : XNet.xNetApi.getChannels().values()) {
                        type.choices(channelType.getID());       // Show names?
                    }
                    Button create = button(100, 3, 53, 14, TranslateHelper.getText("inet.controller.channel_editor_panel.button.create.name"))
                            .event(() -> createChannel(type.getCurrentChoice()));

                    Button paste = button(100, 17, 53, 14, TranslateHelper.getText("inet.controller.channel_editor_panel.button.paste.name"))
                            .tooltips(TranslateHelper.getText("inet.controller.channel_editor_panel.button.paste.tooltip1"), TranslateHelper.getText("inet.controller.channel_editor_panel.button.paste.tooltip2"))
                            .event(this::pasteChannel);

                    channelEditPanel.children(type, create, paste);
                }
            }
        } else if (showingChannel != -1 && editingChannel == -1) {
            showingChannel = -1;
            channelEditPanel.removeChildren();
        }

        ci.cancel();
    }

    @Inject(method = "copyChannel", at = @At(value = "HEAD"), remap = false)
    private void mixin$copyChannel(CallbackInfo ci) {
        GuiController.showMessage(this.minecraft, this, this.getWindowManager(), 50, 50, ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.copy_channel.massage"));
        this.sendServerCommandTyped(XNetMessages.INSTANCE, TileEntityController.CMD_COPYCHANNEL, TypedMap.builder().put(TileEntityController.PARAM_INDEX, this.getSelectedChannel()).build());
    }

    @Inject(method = "toClipboard", at = @At(value = "HEAD"), remap = false, cancellable = true)
    private static void toClipboard(String json, CallbackInfo ci) {
        try {
            Minecraft.getInstance().keyboardHandler.setClipboard(json);
        } catch (Exception var2) {
            GuiController.showError(TranslateHelper.getText("inet.controller.clipboard.massage.error"));
        }

        ci.cancel();
    }

    @Inject(method = "pasteConnector", at = @At(value = "HEAD"), remap = false, cancellable = true)
    private void mixin$pasteConnector(CallbackInfo ci) {
        try {
            String json = Minecraft.getInstance().keyboardHandler.getClipboard();
            int max = Config.controllerMaxPaste.get();
            if (max >= 0 && json.length() > max) {
                GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.long"));
                ci.cancel();
                return;
            }
            JsonParser parser = new JsonParser();
            JsonObject root = parser.parse(json).getAsJsonObject();
            String type = root.get("type").getAsString();
            IChannelType channelType = XNet.xNetApi.findType(type);
            if (channelType == null) {
                GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.unsupported") + " " + type + "!");
                ci.cancel();
                return;
            }

            PacketServerCommandTyped packet = new PacketServerCommandTyped(tileEntity.getBlockPos(), tileEntity.getDimension(), TileEntityController.CMD_PASTECONNECTOR.name(), TypedMap.builder()
                    .put(TileEntityController.PARAM_INDEX, getSelectedChannel())
                    .put(TileEntityController.PARAM_POS, editingConnector.pos())
                    .put(TileEntityController.PARAM_SIDE, editingConnector.side().ordinal())
                    .put(TileEntityController.PARAM_JSON, json)
                    .build());
            sendSplit(packet, max < 0);

            if (connectorList.getSelected() != -1) {
                delayedSelectedChannel = getSelectedChannel();
                delayedSelectedLine = connectorList.getSelected();
                delayedSelectedConnector = connectorPositions.get(connectorList.getSelected());
            }
            refresh();
        } catch (Exception e) {
            GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.error_read"));
        }

        ci.cancel();
    }

    @Inject(method = "pasteChannel", at = @At(value = "HEAD"), remap = false, cancellable = true)
    private void mixin$pasteChannel(CallbackInfo ci) {
        try {
            String json = Minecraft.getInstance().keyboardHandler.getClipboard();
            int max = Config.controllerMaxPaste.get();
            if (max >= 0 && json.length() > max) {
                GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.long"));
                ci.cancel();
                return;
            }
            JsonParser parser = new JsonParser();
            JsonObject root = parser.parse(json).getAsJsonObject();
            String type = root.get("type").getAsString();
            IChannelType channelType = XNet.xNetApi.findType(type);
            if (channelType == null) {
                GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.unsupported") + " " + type + "!");
                ci.cancel();
                return;
            }
            PacketServerCommandTyped packet = new PacketServerCommandTyped(tileEntity.getBlockPos(), tileEntity.getDimension(), TileEntityController.CMD_PASTECHANNEL.name(), TypedMap.builder()
                    .put(TileEntityController.PARAM_INDEX, getSelectedChannel())
                    .put(TileEntityController.PARAM_JSON, json)
                    .build());
            sendSplit(packet, max < 0);

            refresh();
        } catch (Exception e) {
            GuiController.showMessage(minecraft, this, getWindowManager(), 50, 50, ChatFormatting.RED + TranslateHelper.getText("inet.controller.clipboard.massage.error_read"));
        }

        ci.cancel();
    }

    @Inject(method = "refreshConnectorEditor", at = @At(value = "HEAD"), remap = false, cancellable = true)
    private void mixin$refreshConnectorEditor(CallbackInfo ci) {
        if (!listsReady()) {
            ci.cancel();
            return;
        }
        if (editingConnector != null && !editingConnector.equals(showingConnector)) {
            showingConnector = editingConnector;
            connectorEditPanel.removeChildren();
            ChannelClientInfo info = tileEntity.clientChannels.get(editingChannel);
            if (info != null) {
                ConnectorClientInfo clientInfo = findClientInfo(info, editingConnector);
                if (clientInfo != null) {
                    Direction side = clientInfo.getPos().side();
                    SidedConsumer sidedConsumer = new SidedConsumer(clientInfo.getConsumerId(), side.getOpposite());
                    ConnectorClientInfo connectorInfo = info.getConnectors().get(sidedConsumer);

                    Button remove = button(151, 1, 9, 10, "x")
                            .textOffset(0, -1)
                            .tooltips(TranslateHelper.getText("inet.controller.connector_edit_panel.button.remove.tooltip"))
                            .event(() -> removeConnector(editingConnector));

                    ConnectorEditorPanel editor = new ConnectorEditorPanel(connectorEditPanel, minecraft, ((GuiController) (Object) this), editingChannel, editingConnector);

                    connectorInfo.getConnectorSettings().createGui(editor);
                    connectorEditPanel.children(remove);
                    editor.setState(connectorInfo.getConnectorSettings());
                } else {
                    Button create = button(85, 20, 60, 14, TranslateHelper.getText("inet.controller.connector_edit_panel.button.create.name"))
                            .event(() -> createConnector(editingConnector));
                    connectorEditPanel.children(create);

                    Button paste = button(85, 40, 60, 14, TranslateHelper.getText("inet.controller.connector_edit_panel.button.paste.name"))
                            .tooltips(TranslateHelper.getText("inet.controller.connector_edit_panel.button.paste.tooltip1"), TranslateHelper.getText("inet.controller.connector_edit_panel.button.paste.tooltip2"))
                            .event(this::pasteConnector);
                    connectorEditPanel.children(paste);
                }
            }
        } else if (showingConnector != null && editingConnector == null) {
            showingConnector = null;
            connectorEditPanel.removeChildren();
        }

        ci.cancel();
    }

    @Inject(method = "populateList", at = @At(value = "HEAD"), remap = false, cancellable = true)
    private void mixin$populateList(CallbackInfo ci) {
        if (!listsReady()) {
            ci.cancel();
            return;
        }
        if (!needsRefresh) {
            ci.cancel();
            return;
        }
        needsRefresh = false;

        connectorList.removeChildren();
        connectorPositions.clear();

        int sel = connectorList.getSelected();
        BlockPos prevPos = null;

        String selectedText = searchBar.getText().trim().toLowerCase();

        for (ConnectedBlockClientInfo connectedBlock : tileEntity.clientConnectedBlocks) {
            SidedPos sidedPos = connectedBlock.getPos();
            BlockPos coordinate = sidedPos.pos();
            String name = connectedBlock.getName();
            String blockUnlocName = connectedBlock.getBlockUnlocName();
            String blockName = I18n.get(blockUnlocName).trim();

            int color = StyleConfig.colorTextInListNormal;

            Panel panel = Widgets.horizontal(0, 0);
            if (!selectedText.isEmpty()) {
                if (blockName.toLowerCase().contains(selectedText)) {
                    panel.filledBackground(0xffddeeaa);
                }
            }
            BlockRender br;
            if (coordinate.equals(prevPos)) {
                br = new BlockRender();
            } else {
                br = new BlockRender().renderItem(connectedBlock.getConnectedBlock());
                prevPos = coordinate;
            }
            br.userObject("block");
            panel.children(br);
            if (!name.isEmpty()) {
                br.tooltips(ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.block_list.tooltip.connector") + " " + ChatFormatting.WHITE + name,
                        ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.block_list.tooltip.block") + " " + ChatFormatting.WHITE + blockName,
                        ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.block_list.tooltip.position") + " " + ChatFormatting.WHITE + BlockPosTools.toString(coordinate),
                        ChatFormatting.WHITE + TranslateHelper.getText("inet.controller.block_list.tooltip.highlight"));
            } else {
                br.tooltips(ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.block_list.tooltip.block") + " " + ChatFormatting.WHITE + blockName,
                        ChatFormatting.GREEN + TranslateHelper.getText("inet.controller.block_list.tooltip.position") + " " + ChatFormatting.WHITE + BlockPosTools.toString(coordinate),
                        ChatFormatting.WHITE + TranslateHelper.getText("inet.controller.block_list.tooltip.highlight"));
            }

            panel.children(Widgets.label(sidedPos.side().getSerializedName().substring(0, 1).toUpperCase()).color(color).desiredWidth(18));
            for (int i = 0; i < ChannelInfo.MAX_CHANNELS; i++) {
                Button but = new Button().desiredWidth(14);
                ChannelClientInfo info = tileEntity.clientChannels.get(i);
                if (info != null) {
                    ConnectorClientInfo clientInfo = findClientInfo(info, sidedPos);
                    if (clientInfo != null) {
                        IndicatorIcon icon = clientInfo.getConnectorSettings().getIndicatorIcon();
                        if (icon != null) {
                            but.image(icon.getImage(), icon.getU(), icon.getV(), icon.getIw(), icon.getIh());
                        }
                        String indicator = clientInfo.getConnectorSettings().getIndicator();
                        but.text(indicator != null ? indicator : "");
                    }
                }
                int finalI = i;
                but.event(() -> selectConnectorEditor(sidedPos, finalI));
                panel.children(but);
            }
            connectorList.children(panel);
            connectorPositions.add(sidedPos);
        }

        connectorList.selected(sel);
        if (delayedSelectedChannel != -1) {
            connectorList.selected(delayedSelectedLine);
            selectConnectorEditor(delayedSelectedConnector, delayedSelectedChannel);
        }
        delayedSelectedChannel = -1;
        delayedSelectedLine = -1;
        delayedSelectedConnector = null;

        ci.cancel();
    }
}
